package com.yyysj.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yyysj.common.core.constants.Constants;
import com.yyysj.common.core.domain.Result;
import com.yyysj.common.core.domain.TableDataInfo;
import com.yyysj.common.core.enums.ResultCode;
import com.yyysj.common.security.exception.ServiceException;
import com.yyysj.system.domain.dto.ExamAddDTO;
import com.yyysj.system.domain.dto.ExamEditDTO;
import com.yyysj.system.domain.dto.ExamQueryDTO;
import com.yyysj.system.domain.dto.ExamQuestionAddDTO;
import com.yyysj.system.domain.po.Exam;
import com.yyysj.system.domain.po.ExamQuestion;
import com.yyysj.system.domain.po.Question;
import com.yyysj.system.domain.vo.ExamDetailVO;
import com.yyysj.system.domain.vo.ExamVO;
import com.yyysj.system.domain.vo.QuestionVO;
import com.yyysj.system.mapper.ExamMapper;
import com.yyysj.system.mapper.ExamQuestionMapper;
import com.yyysj.system.mapper.QuestionMapper;
import com.yyysj.system.service.IExamService;
import com.yyysj.system.manager.ExamCacheManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
@RequiredArgsConstructor
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {

    private final ExamMapper examMapper;
    private final QuestionMapper questionMapper;
    private final ExamQuestionMapper examQuestionMapper;
    private final ExamCacheManager examCacheManager;

    @Override
    public TableDataInfo list(ExamQueryDTO examQueryDTO) {

        log.info("获取竞赛列表的查询参数：{}", examQueryDTO);

        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());

        List<ExamVO> examVOList =examMapper.selectExamList(examQueryDTO);
        Page<ExamVO> page = (Page<ExamVO>) examVOList;

        if(page == null || page.getTotal() <= 0) {
            return TableDataInfo.empty();
        }

        log.info("从数据库查询的竞赛列表分页信息：{}", page.getResult());
        PageInfo<ExamVO> pageInfo = new PageInfo<>(page);
        return TableDataInfo.success(page.getResult(),pageInfo.getTotal());
    }

    @Override
    public Result<String> add(ExamAddDTO examAddDTO) {
        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  //竞赛开始时间不能早于当前时间
        }
        if (examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }

        // 根据竞赛标题判断是否存在
        Exam exam = new Exam();
        BeanUtils.copyProperties(examAddDTO, exam);

        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, exam.getTitle()));
        if (CollectionUtil.isNotEmpty(examList)) {
            return Result.fail(ResultCode.FAILED_ALREADY_EXISTS);
        }
        // 插入数据库
        int rows = examMapper.insert(exam);
        return rows > 0 ? Result.ok(exam.getExamId().toString()) : Result.fail(ResultCode.FAILED);
    }

    @Override
    public Result<?> questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {

        // 判断竞赛是否存在
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        // 判断竞赛是否有效
        checkExam(exam);
        // 判断竞赛是否发布
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        // 判断是否有题目添加到当前竞赛中
        Set<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            return Result.fail();
        }
        // 判断添加的题目是否有
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        boolean res = saveExamQuestion(exam, questionIdSet);
        if(res) {
            return Result.ok();
        } else {
            return Result.fail();
        }
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);

        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        Long count = examQuestionMapper
                .selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);

        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam, examDetailVO);
        List<QuestionVO> questionVOList = examQuestionMapper.selectExamQuestionList(examId);
        if (CollectionUtil.isEmpty(questionVOList)) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSaveParams(examEditDTO, examEditDTO.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);
        checkExam(exam); // 如果竞赛开始了就不能修改题目量
        if (Constants.TRUE.equals(exam.getStatus())) { // 判断是否已经发布
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        // 1、竞赛标题是否重复进行判断   2、竞赛开始、结束时间进行判断
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  // 竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    private void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }

    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        int num = 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
        return this.saveBatch(examQuestionList);
    }
}
